package cn.lh.exception;

import cn.lh.config.Resp;
import cn.lh.contants.ErrorCode;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.NestedRuntimeException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.ResourceAccessException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final String DEFAULT_ERROR_MESSAGE = "服务器出小差了，请重试";

    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Resp<?> processBusinessException(BusinessException e) {
    	log.error("[SERVER ERROR]", e);
        String message = e.getMessage();
        Throwable rootCause = e.getRootCause();
        String finalMsg = message == null ? (rootCause == null ? DEFAULT_ERROR_MESSAGE : rootCause.getMessage()) : message;
        return new Resp<>(e.getCode(), finalMsg);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Resp<?> processConstraintViolationException(ConstraintViolationException e) {
    	log.error("[SERVER ERROR]", e);
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        ConstraintViolation<?> constraintViolation = Lists.newArrayList(constraintViolations).get(0);
        String messageTemplate = constraintViolation.getMessageTemplate();
        return new Resp<>(ErrorCode.BAD_REQUEST.getCode(), messageTemplate);
    }

    @ExceptionHandler(ServletRequestBindingException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Resp<?> processServletRequestBindingException(ServletRequestBindingException e) {
        log.error("[PARAM ERROR]", e);
        return new Resp<>(ErrorCode.BAD_REQUEST.getCode(), e.getMessage());
    }

    @ExceptionHandler(NestedRuntimeException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Resp<?> processNestedRuntimeException(NestedRuntimeException e) {
        log.error("[SERVER ERROR]", e);
        Throwable rootCause = e.getRootCause();
        return new Resp<>(ErrorCode.INTERNAL_SERVER_ERROR.getCode(), rootCause == null ? e.getMessage() : rootCause.getMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Resp<?> processOtherException(Exception e) {
        log.error("[UNKNOWN ERROR]", e);
        return new Resp<>(ErrorCode.INTERNAL_SERVER_ERROR.getCode(), ErrorCode.INTERNAL_SERVER_ERROR.getMessage(), e.getMessage());
    }
    
	@ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Resp<?> exception(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        if(allErrors.size() > 0) {
            FieldError fieldError = (FieldError) allErrors.get(0);
            return new Resp<>(ErrorCode.BAD_REQUEST.getCode(), fieldError.getDefaultMessage());
        }
		return null;
    }
	
	@ExceptionHandler(ResourceAccessException.class)
    @ResponseBody
    public Resp<?> exception(ResourceAccessException e) {
		log.error("[READ TIMED OUT]", e);
        return new Resp<>(ErrorCode.TIME_OUT.getCode(), ErrorCode.TIME_OUT.getMessage());
    }
	
	@ExceptionHandler(DataIntegrityViolationException.class)
    @ResponseBody
    public Resp<?> exception(DataIntegrityViolationException e) {
		log.error("[DATABASE EXCEPTION]", e);
        return new Resp<>(ErrorCode.INTERNAL_SERVER_ERROR.getCode(), ErrorCode.INTERNAL_SERVER_ERROR.getMessage());
    }
	
	@ExceptionHandler(BadSqlGrammarException.class)
    @ResponseBody
    public Resp<?> exception(BadSqlGrammarException e) {
		log.error("[BAD SQL EXCEPTION]", e);
        return new Resp<>(ErrorCode.INTERNAL_SERVER_ERROR.getCode(), ErrorCode.INTERNAL_SERVER_ERROR.getMessage());
    }
	
	@ExceptionHandler(HttpClientErrorException.class)
    @ResponseBody
    public Resp<?> exception(HttpClientErrorException e) {
		log.error("[HTTP CLIENT ERROR EXCEPTION]", e);
        return new Resp<>(ErrorCode.INTERNAL_SERVER_ERROR.getCode(), ErrorCode.INTERNAL_SERVER_ERROR.getMessage());
    }
	
	@ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public Resp<?> exception(HttpMessageNotReadableException e) {
		log.error("[PARAM TYPE ERROR]", e);
        return new Resp<>(ErrorCode.BAD_REQUEST.getCode(), e.getMessage());
    }
	
	
	
	
}